﻿using BarManager.Core.BusinessRules.Interfaces.Specification;
using BarManager.Core.DomainService;
using System.Collections.Generic;
using System.Linq;

namespace BarManager.Core.BusinessRules.Specification
{
    public class EnvioUpdateSpecification : BaseSpecification<Entity.Envio>, IEnvioSpecification
    {
        private IEnumerable<Entity.EnvioDetalleBase> Detalles { get; set; }
        private IProductoRepository ProductoRepository { get; set; }
        public EnvioUpdateSpecification(IEnvioRepository repository, IProductoRepository productoRepository)
            : base(repository)
        {
            this.ProductoRepository = productoRepository;
        }

        public override bool IsSatisfiedBy(Entity.Envio entity)
        {
            //- Orden a validar
            base.Entity = entity;

            //- Orden en bd
            Detalles = BaseRepository.Get(x => x.Id == entity.Id).Detalle;

            //- Validamos items en memoria
            foreach (var item in Entity.Detalle)
            {
                //- Validamos productos
                if (item is Entity.EnvioDetalleProducto)
                {
                    int stockActual = 0;

                    //- Buscamos si existe el item en BD
                    var aux = Detalles.FirstOrDefault(x => x.Guid == item.Guid);

                    //- Si existe y cantidad en memoria es > q bd valida stock
                    if (aux != null && aux.Cantidad < item.Cantidad)
                        if (!TieneStockSuficiente((aux as Entity.EnvioDetalleProducto).Producto, item.Cantidad - aux.Cantidad, out stockActual)) return false;

                    //- Si no existe en bd validamos tmb
                    if (aux == null)
                        if (!TieneStockSuficiente((item as Entity.EnvioDetalleProducto).Producto, item.Cantidad, out stockActual)) return false;
                }

                //- Validamos promociones
                if (item is Entity.EnvioDetallePromocion)
                {
                    foreach (var promo in (item as Entity.EnvioDetallePromocion).Promocion.PromocionDetalleList)
                    {
                        int stockActual = 0;

                        //- Buscamos si existe el item en BD
                        var aux = Detalles.FirstOrDefault(x => x.Guid == item.Guid);

                        //- Si existe y cantidad en memoria es > q bd valida stock
                        if (aux != null && aux.Cantidad < item.Cantidad)
                            if (!TieneStockSuficiente(promo.Producto, (item.Cantidad * promo.Cantidad) - (aux.Cantidad * promo.Cantidad), out stockActual)) return false;

                        //- Si no existe en bd validamos tmb
                        if (aux == null)
                            if (!TieneStockSuficiente(promo.Producto, (item.Cantidad * promo.Cantidad), out stockActual)) return false;
                    }
                }
            }
            return true;
        }

        public override IEnumerable<string> GetErrorMessage()
        {
            //- Recorremos detalle en memoria
            foreach (var item in Entity.Detalle)
            {
                //- Validamos productos
                if (item is Entity.EnvioDetalleProducto)
                {
                    int stockActual = 0;

                    //- Buscamos si existe el item en BD
                    var aux = Detalles.FirstOrDefault(x => x.Guid == item.Guid);

                    //- Si existe y cantidad en memoria es > q bd valida stock
                    if (aux != null && aux.Cantidad < item.Cantidad)
                        if (!TieneStockSuficiente((aux as Entity.EnvioDetalleProducto).Producto, item.Cantidad - aux.Cantidad, out stockActual))
                            yield return string.Format("El producto {0} tiene un stock actual de {1} items e intenta agregar {2} items..", (aux as Entity.EnvioDetalleProducto).Producto.Nombre, stockActual, item.Cantidad - aux.Cantidad);

                    //- Si no existe en bd validamos tmb
                    if (aux == null)
                        if (!TieneStockSuficiente((item as Entity.EnvioDetalleProducto).Producto, item.Cantidad, out stockActual))
                            yield return string.Format("El producto {0} tiene un stock actual de {1} items e intenta agregar {2} items.", (item as Entity.EnvioDetalleProducto).Producto.Nombre, stockActual, item.Cantidad);
                }

                //- Validamos promociones
                if (item is Entity.EnvioDetallePromocion)
                {
                    foreach (var promo in (item as Entity.EnvioDetallePromocion).Promocion.PromocionDetalleList)
                    {
                        int stockActual = 0;

                        //- Buscamos si existe el item en BD
                        var aux = Detalles.FirstOrDefault(x => x.Guid == item.Guid);

                        //- Si existe y cantidad en memoria es > q bd valida stock
                        if (aux != null && aux.Cantidad < item.Cantidad)
                            if (!TieneStockSuficiente(promo.Producto, (item.Cantidad * promo.Cantidad) - (aux.Cantidad * promo.Cantidad), out stockActual))
                                yield return string.Format("La promocion {0} no tiene stock para el producto {1}.", promo.Promocion.Descripcion, promo.Producto.Nombre);

                        //- Si no existe en bd validamos
                        if (aux == null)
                            if (!TieneStockSuficiente(promo.Producto, (item.Cantidad * promo.Cantidad), out stockActual))
                                yield return string.Format("La promocion {0} no tiene stock para el producto {1}.", promo.Promocion.Descripcion, promo.Producto.Nombre);
                    }
                }
            }
            yield break;
        }

        private bool TieneStockSuficiente(Entity.Producto producto, int cantADescontar, out int stockActual)
        {
            stockActual = 0;
            var prod = ProductoRepository.Get(x => x.Id == producto.Id);

            //- Si no existe producto sale
            if (prod == null) return false;

            //- Si no controla stock sale
            if (!prod.ControlarStock) return true;

            //- Devolemos stock actual y si es suficiente para el pedido
            stockActual = prod.Stock.Value;
            return (prod.Stock >= cantADescontar);
        }

    }
}
